package com.mpgame.ctrls;

import java.sql.Timestamp;
import java.util.Objects;

import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.mpgame.common.proto.CmdId;
import com.mpgame.common.proto.ErrorCode;
import com.mpgame.common.proto.GamePacket;
import com.mpgame.common.proto.LoginReq;
import com.mpgame.common.proto.LoginRsp;
import com.mpgame.common.proto.RegistReq;
import com.mpgame.common.proto.RegistRsp;
import com.mpgame.common.utils.Log;
import com.mpgame.database.mysql.mapper.verify.AccountMapper;
import com.mpgame.database.mysql.model.verify.Account;
import com.mpgame.database.mysql.service.verify.AccountService;
import com.mpgame.database.redis.model.UserTokenInfo;
import com.mpgame.database.redis.utils.PlayerOnlineUtil;
import com.mpgame.database.redis.utils.RedisUtil;
import com.mpgame.database.redis.utils.TokenUtil;

import jakarta.servlet.http.HttpServletRequest;
import com.mpgame.utils.IpUtils;
import com.mpgame.utils.JwtUtil;

@RestController
@RequestMapping("/api/verify")
@CrossOrigin(origins = "*")
public class VerifyCtrl {

    @Autowired
    @Qualifier("verifySqlSessionFactory")
    private SqlSessionFactory verifySqlSessionFactory;

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping(value = "/login", consumes = "application/x-protobuf", produces = "application/x-protobuf")
    public byte[] login(@RequestBody byte[] request, HttpServletRequest httpRequest) {
        try {
            LoginReq req = LoginReq.parseFrom(GamePacket.parseFrom(request).getData());

            try (var sqlSession = verifySqlSessionFactory.openSession()) {
                AccountMapper mapper = sqlSession.getMapper(AccountMapper.class);
                Account acct = mapper.selectByUsername(req.getAcct());
                if (acct == null) {
                    Log.warn("登陆失败：账号不存在！" + req.getAcct());
                    return getLoginErrRsp("账号不存在！");
                }

                if (!Objects.equals(req.getPwd(), acct.getPassword())) {
                    Log.warn("登陆失败：密码错误 - " + req.getAcct());
                    return getLoginErrRsp("密码错误！");
                }

                if (acct.getStatus() != 0) {
                    Log.warn("登陆失败，账号已被禁用 - " + req.getAcct());
                    return getLoginErrRsp("账号已被禁用");
                }

                mapper.updateLastLogin(acct.getUsername(), new Timestamp(System.currentTimeMillis()),
                        IpUtils.getClientIp(httpRequest));
                sqlSession.commit();

                UserTokenInfo userTokenInfo = new UserTokenInfo();
                userTokenInfo.setAccountId(acct.getId());
                userTokenInfo.setUsername(acct.getUsername());
                userTokenInfo.setNickname(acct.getNickname());
                userTokenInfo.setLastLoginTimestamp(System.currentTimeMillis());
                userTokenInfo.setLastActiveTimestamp(System.currentTimeMillis());

                String jwtToken = jwtUtil.generateToken(userTokenInfo);
                if (jwtToken == null) {
                    Log.err("JWT Token生成失败：" + req.getAcct());
                    return getLoginErrRsp("登录失败");
                }

                String redisToken = TokenUtil.genToken(userTokenInfo);
                if (redisToken == null) {
                    Log.err("Redis Token生成失败：" + req.getAcct());
                    return getLoginErrRsp("登录失败");
                }

                Log.info("用户登录成功：" + req.getAcct());
                return getLoginSucRsp("登录成功", redisToken, jwtToken);
            }

        } catch (Exception e) {
            Log.err("登录处理异常：" + e.getMessage());
            return getLoginErrRsp("登录失败！");
        }
    }

    @PostMapping(value = "/regist", consumes = "application/x-protobuf", produces = "application/x-protobuf")
    public byte[] regist(@RequestBody byte[] request, HttpServletRequest httpRequest) {
        try {
            RegistReq req = RegistReq.parseFrom(GamePacket.parseFrom(request).getData());

            try (var sqlSession = verifySqlSessionFactory.openSession()) {
                AccountMapper mapper = sqlSession.getMapper(AccountMapper.class);
                Account acct = mapper.selectByUsername(req.getAcct());

                if (acct != null) {
                    Log.warn("注册失败：账号已存在！" + req.getAcct());
                    return getRegistErrRsp("注册失败：账号已存在！" + req.getAcct());
                }

                Account newAcct = new Account();
                newAcct.setUsername(req.getAcct());
                newAcct.setPassword(req.getPwd());
                newAcct.setAvatarUrl(req.getAvatarUrl());
                newAcct.setEmail(req.getEmail());
                newAcct.setPhone(req.getPhone());
                newAcct.setNickname(req.getNickname());
                newAcct.setStatus(0);
                newAcct.setCreatedAt(new Timestamp(System.currentTimeMillis()));
                newAcct.setLastLoginAt(new Timestamp(System.currentTimeMillis()));
                newAcct.setLastLoginIp(IpUtils.getClientIp(httpRequest));

                int result = mapper.insert(newAcct);
                if (result <= 0) {
                    Log.err("注册失败：数据库插入失败 - " + req.getAcct());
                    return getRegistErrRsp("注册失败");
                }
                sqlSession.commit();
                // 创建用户Token信息
                UserTokenInfo userTokenInfo = new UserTokenInfo();
                userTokenInfo.setAccountId(newAcct.getId());
                userTokenInfo.setUsername(newAcct.getUsername());
                userTokenInfo.setNickname(newAcct.getNickname());
                userTokenInfo.setLastLoginTimestamp(System.currentTimeMillis());
                userTokenInfo.setLastActiveTimestamp(System.currentTimeMillis());

                // 生成JWT Token
                String jwtToken = jwtUtil.generateToken(userTokenInfo);
                if (jwtToken == null) {
                    Log.err("JWT Token生成失败：" + req.getAcct());
                    return getRegistErrRsp("注册失败");
                }
                // 生成Redis Token
                String redisToken = TokenUtil.genToken(userTokenInfo);
                if (redisToken == null) {
                    Log.err("Redis Token生成失败：" + req.getAcct());
                    return getRegistErrRsp("注册失败");
                }

                Log.info("用户注册成功：" + req.getAcct());
                return getRegistSucRsp("注册成功", redisToken, jwtToken);
            }
        } catch (Exception e) {
            Log.err("注册处理异常：" + e.getMessage());
            return getRegistErrRsp("注册失败！");
        }
    }

    private byte[] getLoginSucRsp(String msg, String redisToken, String jwtToken) {
        return GamePacket.newBuilder()
                .setCmd(CmdId.LOGIN_RSP)
                .setData(LoginRsp.newBuilder()
                        .setErrMsg(msg)
                        .setToken(redisToken)
                        .setJwtToken(jwtToken)
                        .build()
                        .toByteString())
                .build()
                .toByteArray();
    }

    private byte[] getLoginErrRsp(String msg) {
        return GamePacket.newBuilder()
                .setCmd(CmdId.LOGIN_RSP)
                .setEc(ErrorCode.INVALID_PARAM)
                .setData(LoginRsp.newBuilder()
                        .setErrMsg(msg)
                        .build()
                        .toByteString())
                .build()
                .toByteArray();
    }

    private byte[] getRegistSucRsp(String msg, String redisToken, String jwtToken) {
        return GamePacket.newBuilder()
                .setCmd(CmdId.REGIST_RSP)
                .setData(RegistRsp.newBuilder()
                        .setErrMsg(msg)
                        .setToken(redisToken)
                        .setJwtToken(jwtToken)
                        .build()
                        .toByteString())
                .build()
                .toByteArray();
    }

    private byte[] getRegistErrRsp(String msg) {
        return GamePacket.newBuilder()
                .setCmd(CmdId.REGIST_RSP)
                .setEc(ErrorCode.INVALID_PARAM)
                .setData(RegistRsp.newBuilder()
                        .setErrMsg(msg)
                        .build()
                        .toByteString())
                .build()
                .toByteArray();
    }

}
